package carapace

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
	
	
	
)

// ActionCallback invokes a go function during completion.
func ( CompletionCallback) Action {
	return Action{callback: }
}

// ActionExecCommand executes an external command.
//
//	carapace.ActionExecCommand("git", "remote")(func(output []byte) carapace.Action {
//	  lines := strings.Split(string(output), "\n")
//	  return carapace.ActionValues(lines[:len(lines)-1]...)
//	})
func ( string,  ...string) func( func( []byte) Action) Action {
	return func( func( []byte) Action) Action {
		return ActionExecCommandE(, ...)(func( []byte,  error) Action {
			if  != nil {
				if ,  := .(*exec.ExitError);  {
					if  := strings.SplitN(string(.Stderr), "\n", 2)[0]; strings.TrimSpace() != "" {
						 = errors.New()
					}
				}
				return ActionMessage(.Error())
			}
			return ()
		})
	}
}

// ActionExecCommandE is like ActionExecCommand but with custom error handling.
//
//	carapace.ActionExecCommandE("supervisorctl", "--configuration", path, "status")(func(output []byte, err error) carapace.Action {
//		if err != nil {
//			const NOT_RUNNING = 3
//			if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.ExitCode() != NOT_RUNNING {
//				return carapace.ActionMessage(err.Error())
//			}
//		}
//		return carapace.ActionValues("success")
//	})
func ( string,  ...string) func( func( []byte,  error) Action) Action {
	return func( func( []byte,  error) Action) Action {
		return ActionCallback(func( Context) Action {
			var ,  bytes.Buffer
			 := .Command(, ...)
			.Stdout = &
			.Stderr = &
			if  := .Run();  != nil {
				if ,  := .(*exec.ExitError);  {
					.Stderr = .Bytes() // seems this needs to be set manually due to stdout being collected?
				}
				return (.Bytes(), )
			}
			return (.Bytes(), nil)
		})
	}
}

// ActionImport parses the json output from export as Action
//
//	carapace.Gen(rootCmd).PositionalAnyCompletion(
//		carapace.ActionCallback(func(c carapace.Context) carapace.Action {
//			args := []string{"_carapace", "export", ""}
//			args = append(args, c.Args...)
//			args = append(args, c.Value)
//			return carapace.ActionExecCommand("command", args...)(func(output []byte) carapace.Action {
//				return carapace.ActionImport(output)
//			})
//		}),
//	)
func ( []byte) Action {
	return ActionCallback(func( Context) Action {
		var  export.Export
		if  := json.Unmarshal(, &);  != nil {
			return ActionMessage(.Error())
		}
		return Action{
			rawValues: .Values,
			meta:      .Meta,
		}
	})
}

// ActionExecute executes completion on an internal command
// TODO example.
func ( *cobra.Command) Action {
	return ActionCallback(func( Context) Action {
		 := []string{"_carapace", "export", .Name()}
		 = append(, .Args...)
		 = append(, .Value)
		.SetArgs()

		Gen().PreInvoke(func( *cobra.Command,  *pflag.Flag,  Action) Action {
			return ActionCallback(func( Context) Action {
				.Env = .Env
				.Dir = .Dir
				return .Invoke().ToA()
			})
		})

		var ,  bytes.Buffer
		.SetOut(&)
		.SetErr(&)

		if  := .Execute();  != nil {
			return ActionMessage(.Error())
		}
		return ActionImport(.Bytes())
	})
}

// ActionDirectories completes directories.
func () Action {
	return ActionCallback(func( Context) Action {
		return actionPath([]string{""}, true).
			MultiParts("/").
			StyleF(style.ForPath).
			UidF(func( string,  uid.Context) (*url.URL, error) { // TODO duplicated from ActionFiles
				,  := .Abs()
				if  != nil {
					return nil, 
				}
				return url.Parse("file://" + )
			})
	}).Tag("directories")
}

// ActionFiles completes files with optional suffix filtering.
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		return actionPath(, false).
			MultiParts("/").
			StyleF(style.ForPath).
			UidF(func( string,  uid.Context) (*url.URL, error) {
				,  := .Abs()
				if  != nil {
					return nil, 
				}
				return url.Parse("file://" + )
			})
	}).Tag("files")
}

// ActionValues completes arbitrary keywords (values).
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		 := make([]common.RawValue, 0, len())
		for ,  := range  {
			if  != "" {
				 = append(, common.RawValue{Value: , Display: })
			}
		}
		return Action{rawValues: }
	})
}

// ActionStyledValues is like ActionValues but also accepts a style.
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		if  := len(); %2 != 0 {
			return ActionMessage("invalid amount of arguments [ActionStyledValues]: %v", )
		}

		 := make([]common.RawValue, 0, len()/2)
		for  := 0;  < len();  += 2 {
			 = append(, common.RawValue{Value: [], Display: [], Style: [+1]})
		}
		return Action{rawValues: }
	})
}

// ActionValuesDescribed completes arbitrary key (values) with an additional description (value, description pairs).
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		if  := len(); %2 != 0 {
			return ActionMessage("invalid amount of arguments [ActionValuesDescribed]: %v", )
		}

		 := make([]common.RawValue, 0, len()/2)
		for  := 0;  < len();  += 2 {
			 = append(, common.RawValue{Value: [], Display: [], Description: [+1]})
		}
		return Action{rawValues: }
	})
}

// ActionStyledValuesDescribed is like ActionValues but also accepts a style.
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		if  := len(); %3 != 0 {
			return ActionMessage("invalid amount of arguments [ActionStyledValuesDescribed]: %v", )
		}

		 := make([]common.RawValue, 0, len()/3)
		for  := 0;  < len();  += 3 {
			 = append(, common.RawValue{Value: [], Display: [], Description: [+1], Style: [+2]})
		}
		return Action{rawValues: }
	})
}

// ActionMessage displays a help messages in places where no completions can be generated.
func ( string,  ...interface{}) Action {
	return ActionCallback(func( Context) Action {
		if len() > 0 {
			 = fmt.Sprintf(, ...)
		}
		 := ActionValues()
		.meta.Messages.Add(stripansi.Strip())
		return 
	})
}

// ActionMultiParts completes parts of an argument separated by sep.
func ( string,  func( Context) Action) Action {
	return ActionMultiPartsN(, -1, )
}

// ActionMultiPartsN is like ActionMultiParts but limits the number of parts to `n`.
func ( string,  int,  func( Context) Action) Action {
	return ActionCallback(func( Context) Action {
		switch  {
		case 0:
			return ActionMessage("invalid value for n [ActionValuesDescribed]: %v", )
		case 1:
			return ().Invoke().ToA()
		}

		 := strings.SplitN(.Value, , )
		 := ""
		.Parts = []string{}

		switch {
		case len() == 0:
			switch {
			case  < 0:
				 = .Value
				.Value = ""
				.Parts = 
			default:
				 = .Value
				if -1 < len() {
					 = .Value[:-1]
					.Value = .Value[-1:]
				} else {
					.Value = ""
				}
				.Parts = strings.Split(, "")
			}
		default:
			if len() > 1 {
				.Value = [len()-1]
				.Parts = [:len()-1]
				 = strings.Join(.Parts, ) + 
			}
		}

		 := '*'
		if  := []rune(); len() > 0 {
			 = [len()-1]
		}
		return ().Invoke().Prefix().ToA().NoSpace()
	})
}

// ActionStyleConfig completes style configuration
//
//	carapace.Value=blue
//	carapace.Description=magenta
func () Action {
	return ActionMultiParts("=", func( Context) Action {
		switch len(.Parts) {
		case 0:
			return ActionMultiParts(".", func( Context) Action {
				switch len(.Parts) {
				case 0:
					return ActionValues(config.GetStyleConfigs()...).Invoke().Suffix(".").ToA()

				case 1:
					,  := config.GetStyleFields(.Parts[0])
					if  != nil {
						return ActionMessage(.Error())
					}
					 := Batch()
					for ,  := range  {
						 = append(, ActionStyledValuesDescribed(.Name, .Description, .Style).Tag(.Tag))
					}
					return .Invoke().Merge().Suffix("=").ToA()

				default:
					return ActionValues()
				}
			})
		case 1:
			return ActionMultiParts(",", func( Context) Action {
				return ActionStyles(.Parts...).Invoke().Filter(.Parts...).ToA().NoSpace()
			})
		default:
			return ActionValues()
		}
	})
}

// Actionstyles completes styles
//
//	blue
//	bg-magenta
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		 := false
		 := false

		for ,  := range  {
			if strings.HasPrefix(, "bg-") {
				 = true
			}
			if  == style.Black ||
				 == style.Red ||
				 == style.Green ||
				 == style.Yellow ||
				 == style.Blue ||
				 == style.Magenta ||
				 == style.Cyan ||
				 == style.White ||
				 == style.BrightBlack ||
				 == style.BrightRed ||
				 == style.BrightGreen ||
				 == style.BrightYellow ||
				 == style.BrightBlue ||
				 == style.BrightMagenta ||
				 == style.BrightCyan ||
				 == style.BrightWhite ||
				strings.HasPrefix(, "#") ||
				strings.HasPrefix(, "color") ||
				strings.HasPrefix(, "fg-") {
				 = true
			}
		}

		 := Batch()
		 := func( string) string {
			return style.Of(append(, )...)
		}

		if ! {
			 = append(, ActionStyledValues(
				style.Black, (style.Black),
				style.Red, (style.Red),
				style.Green, (style.Green),
				style.Yellow, (style.Yellow),
				style.Blue, (style.Blue),
				style.Magenta, (style.Magenta),
				style.Cyan, (style.Cyan),
				style.White, (style.White),

				style.BrightBlack, (style.BrightBlack),
				style.BrightRed, (style.BrightRed),
				style.BrightGreen, (style.BrightGreen),
				style.BrightYellow, (style.BrightYellow),
				style.BrightBlue, (style.BrightBlue),
				style.BrightMagenta, (style.BrightMagenta),
				style.BrightCyan, (style.BrightCyan),
				style.BrightWhite, (style.BrightWhite),
			))

			if strings.HasPrefix(.Value, "color") {
				for  := 0;  <= 255; ++ {
					 = append(, ActionStyledValues(
						fmt.Sprintf("color%v", ), (style.XTerm256Color(uint8())),
					))
				}
			} else {
				 = append(, ActionStyledValues("color", style.Of(...)))
			}
		}

		if ! {
			 = append(, ActionStyledValues(
				style.BgBlack, (style.BgBlack),
				style.BgRed, (style.BgRed),
				style.BgGreen, (style.BgGreen),
				style.BgYellow, (style.BgYellow),
				style.BgBlue, (style.BgBlue),
				style.BgMagenta, (style.BgMagenta),
				style.BgCyan, (style.BgCyan),
				style.BgWhite, (style.BgWhite),

				style.BgBrightBlack, (style.BgBrightBlack),
				style.BgBrightRed, (style.BgBrightRed),
				style.BgBrightGreen, (style.BgBrightGreen),
				style.BgBrightYellow, (style.BgBrightYellow),
				style.BgBrightBlue, (style.BgBrightBlue),
				style.BgBrightMagenta, (style.BgBrightMagenta),
				style.BgBrightCyan, (style.BgBrightCyan),
				style.BgBrightWhite, (style.BgBrightWhite),
			))

			if strings.HasPrefix(.Value, "bg-color") {
				for  := 0;  <= 255; ++ {
					 = append(, ActionStyledValues(
						fmt.Sprintf("bg-color%v", ), ("bg-"+style.XTerm256Color(uint8())),
					))
				}
			} else {
				 = append(, ActionStyledValues("bg-color", style.Of(...)))
			}
		}

		 = append(, ActionStyledValues(
			style.Bold, (style.Bold),
			style.Dim, (style.Dim),
			style.Italic, (style.Italic),
			style.Underlined, (style.Underlined),
			style.Blink, (style.Blink),
			style.Inverse, (style.Inverse),
		))

		return .ToA().NoSpace('r')
	}).Tag("styles")
}

// ActionExecutables completes executables either from PATH or given directories
//
//	nvim
//	chmod
func ( ...string) Action {
	return ActionCallback(func( Context) Action {
		if len() == 0 {
			 = strings.Split(os.Getenv("PATH"), string(os.PathListSeparator))
		}
		 := man.Descriptions(.Value) // TODO allow additional descriptions to be registered somewhere for carapace-bin (key, value,...)
		 := Batch()
		for  := len() - 1;  >= 0; -- {
			 = append(, actionDirectoryExecutables([], .Value, ))
		}
		return .ToA().
			UidF(func( string,  uid.Context) (*url.URL, error) {
				return &url.URL{Scheme: "cmd", Host: }, nil
			})
	}).Tag("executables")
}

func actionDirectoryExecutables( string,  string,  map[string]string) Action {
	return ActionCallback(func( Context) Action {
		,  := .Abs()
		if  != nil {
			return ActionMessage(.Error())
		}

		if ,  := os.ReadDir();  == nil {
			 := make([]string, 0)
			for ,  := range  {
				if match.HasPrefix(.Name(), ) {
					if ,  := .Info();  == nil && !.IsDir() && isExecAny(.Mode()) {
						 = append(, .Name(), [.Name()], style.ForPath(+"/"+.Name(), ))
					}
				}
			}
			return ActionStyledValuesDescribed(...).UidF(func( string,  uid.Context) (*url.URL, error) {
				return url.Parse(fmt.Sprintf("file://%v/%v", , )) // TODO trim slash suffix from dir | backslash path possible? (windows)
			})
		}
		return ActionValues()
	})
}

func isExecAny( os.FileMode) bool {
	return &0o111 != 0
}

// ActionPositional completes positional arguments for given command ignoring `--` (dash).
// TODO: experimental - likely gives issues with preinvoke (does not have the full args)
//
//	carapace.Gen(cmd).DashAnyCompletion(
//		carapace.ActionPositional(cmd),
//	)
func ( *cobra.Command) Action {
	return ActionCallback(func( Context) Action {
		if .ArgsLenAtDash() < 0 {
			return ActionMessage("only allowed for dash arguments [ActionPositional]")
		}

		.Args = .Flags().Args()
		 := storage.get()

		var  Action
		if .positionalAny != nil {
			 = *.positionalAny
		}

		if  := len(.Args);  < len(.positional) {
			 = .positional[len(.Args)]
		}
		return .Invoke().ToA()
	})
}

// ActionCommands completes (sub)commands of given command.
// `Context.Args` is used to traverse the command tree further down. Use `Action.Shift` to avoid this.
//
//	carapace.Gen(helpCmd).PositionalAnyCompletion(
//		carapace.ActionCommands(rootCmd),
//	)
func ( *cobra.Command) Action {
	return ActionCallback(func( Context) Action {
		if len(.Args) > 0 {
			for ,  := range .Commands() {
				for ,  := range append(.Aliases, .Name()) {
					if  == .Args[0] { // cmd.Find is too lenient
						return ().Shift(1)
					}
				}
			}
			return ActionMessage("unknown subcommand %#v for %#v", .Args[0], .Name())
		}

		 := Batch()
		for ,  := range .Commands() {
			if (!.Hidden || env.Hidden()) && .Deprecated == "" {
				 := common.Group{Cmd: }
				 = append(, ActionStyledValuesDescribed(.Name(), .Short, .Style()).Tag(.Tag()))
				for ,  := range .Aliases {
					 = append(, ActionStyledValuesDescribed(, .Short, .Style()).Tag(.Tag()))
				}
			}
		}
		return .ToA().UidF(func( string,  uid.Context) (*url.URL, error) {
			 := uid.Command()
			if , ,  := .Find([]string{});  == nil {
				 = .Name() // alias -> actual name
			}

			switch .Path {
			case "":
				.Path = 
			default:
				.Path = .Path + "/" + 
			}
			return , nil
		})
	})
}

// ActionCora bridges given cobra completion function.
func ( func( *cobra.Command,  []string,  string) ([]string, cobra.ShellCompDirective)) Action {
	return ActionCallback(func( Context) Action {
		switch {
		case  == nil:
			return ActionValues()
		case .cmd == nil: // ensure cmd is never nil even if context does not contain one
			LOG.Print("cmd is nil [ActionCobra]")
			.cmd = &cobra.Command{Use: "_carapace_actioncobra", Hidden: true, Deprecated: "dummy command for ActionCobra"}
		}

		if !.cmd.DisableFlagParsing {
			.Args = .cmd.Flags().Args()
		}
		,  := (.cmd, .Args, .Value)
		return compDirective().ToA(...)
	})
}